#include "escalonadorSJF.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int IDprocessosSJF = 0;
lista_t sjfProcessoAtual;
lista_t *listaAptos = NULL;
lista_t *listaRealisados = NULL;

void sjfImprimeResposta() //Função para criar o arquivo de retorno do programa
{
	lista_t *aux;
	int esperaAcumulado = 0, mediaEspera;
	
	aux = listaRealisados;
	while(aux){ //soma todos os tempos de espera para calcular o tempo médio
		esperaAcumulado = esperaAcumulado + aux->tempoEspera;
		aux = aux->next;	
	}
	mediaEspera = esperaAcumulado / (IDprocessosSJF);
	printf("\n%d\n", mediaEspera);
	aux = listaRealisados;
	while(aux){ // imprime os tempos de cpu na ordem de criação dos processos
		printf("%d\n", aux->tempoCpu);
		aux = aux->next;
	}
}

void sjfExecucaoEncerada(int tempo)
{
  lista_t *process;
  if(sjfProcessoAtual.processoId != -1) {
	sjfProcessoAtual.tempoCpu = sjfProcessoAtual.tempoCpu + (tempo - sjfProcessoAtual.tempoInicio);
  
	process = (lista_t*) malloc(sizeof(lista_t)); //aloca processo para ser colocado na lista de escerados
	process->processoId = sjfProcessoAtual.processoId;
	process->next = NULL;
	process->tempoCpu = sjfProcessoAtual.tempoCpu;
	process->tempoEspera = sjfProcessoAtual.tempoEspera;
	process->tempoInicio = 0;
	process->tempoPrev = sjfProcessoAtual.tempoPrev;
  
	listaInsereItemOrdenado(&listaRealisados, process);
	//printf("\nProcesso = %d - tempo de execucao = %d - tempo de espera = %d", sjfProcessoAtual.processoId, sjfProcessoAtual.tempoCpu, sjfProcessoAtual.tempoEspera);
  }//ecrever o tempo do processo
  
  sjfProcessoAtual.processoId = -1;
  sjfExecutaProximo(tempo);
}

void sjfExecutaProximo(int tempo)
{
	lista_t *aux;
  if(listaAptos){
    aux = listaPegaItem(&listaAptos);
	
    if(aux != NULL){
      sjfProcessoAtual.tempoEspera = aux->tempoEspera + (tempo - aux->tempoInicio);
      sjfProcessoAtual.tempoInicio = tempo;
	  sjfProcessoAtual.tempoCpu = aux->tempoCpu;
	  sjfProcessoAtual.processoId = aux->processoId;
	  sjfProcessoAtual.tempoPrev = aux->tempoPrev;
	  free(aux);
    }else sjfProcessoAtual.processoId = -1;
  }else sjfProcessoAtual.processoId = -1;
}


void sjfLiberaCpu(int tempo, float alfa)
{
  lista_t *process;
  float lastCpu;
  float lastPrev;
  
  if(sjfProcessoAtual.processoId != -1) {
	process = (lista_t*) malloc(sizeof(lista_t));
  
	sjfProcessoAtual.tempoCpu = sjfProcessoAtual.tempoCpu + (tempo - sjfProcessoAtual.tempoInicio);
	
	lastPrev = sjfProcessoAtual.tempoPrev; //quarda o tempo da ultima previsao
	lastCpu = tempo - sjfProcessoAtual.tempoInicio;
	
	sjfProcessoAtual.tempoPrev = alfa*lastCpu + (1-alfa)*lastPrev;
  
	process->processoId = sjfProcessoAtual.processoId;
	process->next = NULL;
	process->tempoCpu = sjfProcessoAtual.tempoCpu;
	process->tempoEspera = sjfProcessoAtual.tempoEspera;
	process->tempoInicio = tempo;
	process->tempoPrev =  sjfProcessoAtual.tempoPrev;
  
	sjfProcessoAtual.processoId = -1;
	//printf("\nlibero cpu lastPrev %f lastcpu %f alfa %f tempoPrev = %f  ", lastPrev, lastCpu, alfa, process->tempoPrev); 
	listaInsereItem(&listaAptos, process);
	
	sjfExecutaProximo(tempo);
  }
}

void sjfAddApto(int tempo)
{
  lista_t *process;
  process = (lista_t*) malloc(sizeof(lista_t));

  process->tempoInicio = tempo;
  process->tempoCpu = 0;
  process->tempoEspera = 0;
  process->tempoPrev = 10;
  process->processoId = IDprocessosSJF;
  IDprocessosSJF++;

  if(sjfProcessoAtual.processoId == -1){
    sjfProcessoAtual.next = NULL;
	sjfProcessoAtual.processoId = process->processoId;
	sjfProcessoAtual.tempoInicio = tempo;
	sjfProcessoAtual.tempoEspera = 0;
	sjfProcessoAtual.tempoCpu = 0;
	sjfProcessoAtual.tempoPrev = 10;
	free(process);
  }else{

    listaInsereItem(&listaAptos, process);
  }
}


void listaInsereItemOrdenado(lista_t **lista, lista_t *item)
{ 
  lista_t *aux;
  
  aux = *lista;

  if(*lista == NULL){//se a lista for vazia apenas aponta a lista para o item
    *lista = item;
	(*lista)->next = NULL;
  }
  else {
    while(aux && aux->next && (aux->processoId < item->processoId)) //acha o lugar onde deve ser inserido o item
      aux = aux->next; //em ordem de ID

	if(aux) { // se nao for o ultimo
		item->next = aux->next;
	}else item->next = NULL;
    aux->next = item;
  }
}

void listaInsereItem(lista_t **lista, lista_t *item)
{ //insere item no final da lista
  lista_t *aux;
  
  aux = *lista;

  if(*lista == NULL){//se a lista for vazia apenas aponta a lista para o item
    *lista = item;
	(*lista)->next = NULL;
  }
  else {
    while(aux && aux->next && (aux->next->tempoPrev < item->tempoPrev)) {
        //printf("%f  %f   ", aux->next->tempoPrev, item->tempoPrev);
		aux = aux->next; //procura local onde o tempo de previsao eh maior que o seu tempo
	}
    if(aux){ // se nao for o ultimo
		item->next = aux->next;
	}else item->next = NULL;
    aux->next = item;

  }
}

lista_t* listaPegaItem(lista_t **lista)
{
  lista_t *aux;
  aux = *lista;
  if(*lista)
    *lista = (*lista)->next;
  return aux;
}

void sjfLendoArquivo(char *fileName, int alpha) 
{
    FILE *entrada;
	char buffer[50], *aux;
	int tempo, option;
	float alfa;
	alfa = (alpha*0.01);

	sjfProcessoAtual.processoId = -1;
	
	entrada = fopen(fileName, "r");
	if(entrada == NULL){
		printf("Erro ao abrir o arquivo");
	}else {
	fgets(buffer, 50, entrada);
	while(!feof(entrada)){
		
		aux = strtok(buffer, " ");
		tempo = atoi(aux);
		aux = strtok(NULL, " ");
		option = atoi(aux);
		if(option == 1) {
			sjfAddApto(tempo);
		} else if(option == 2) {
			sjfLiberaCpu(tempo, alfa);
		}else if(option == 3){
			sjfExecucaoEncerada(tempo);
		}
			
		fgets(buffer, 50, entrada);		
	}
	sjfImprimeResposta();
	}
}